﻿using DimensionsHelper.Common.Contracts.Metadata;

namespace DimensionsHelper.Client.WinUi.ViewModels;

public class MetadataDocumentLoadedEventArgs
{
    public MetadataDocumentLoadedEventArgs(string conent, bool isReload)
    {
        Content = conent;
        IsReload = isReload;
    }

    public bool IsReload
    {
        get;
    }


    public string Content
    {
        get;
    }
}

public partial class MetadataDocumentViewModel : RecordHistoryViewModel
{
    public override string Section => ResourceKeys.MdmDocumentDisplayName.GetLocalized();

    protected override string SettingsKey => "Metadata";


    protected override string[] FileFilters =>
    [
        Common.Utils.FileFilters.Mdd
    ];


    private static WeakReference<IProjectTask>? CurrentTask
    {
        get;
        set;
    }


    public ObservableCollection<string> Contexts { get; } = ["Analysis"];


    [ObservableProperty]
    public partial string Context { get; set; } = string.Empty;


    public ObservableCollection<string> Languages { get; } = ["ZH-CN"];


    [ObservableProperty]
    public partial string Language { get; set; } = string.Empty;


    public ObservableCollection<string> LabelTypes { get; } = ["LABEL"];


    [ObservableProperty]
    public partial string LabelType { get; set; } = string.Empty;


    [ObservableProperty]
    public partial bool IsLoading
    {
        get;
        set;
    }


    [ObservableProperty]
    public partial bool IgnoreMetadataSection
    {
        get;
        set;
    }


    [ObservableProperty]
    public partial bool ReadAllTrivia
    {
        get;
        set;
    }


    [ObservableProperty]
    public partial bool ReadHdata
    {
        get;
        set;
    }


    [ObservableProperty]
    public partial bool ReadProperty
    {
        get;
        set;
    }


    [ObservableProperty]
    public partial bool ReadTemplate
    {
        get;
        set;
    }


    [ObservableProperty]
    public partial bool ReadStyle
    {
        get;
        set;
    }


    [ObservableProperty]
    public partial bool ReadLabelStyle
    {
        get;
        set;
    }


    [ObservableProperty]
    public partial bool ReadNote
    {
        get;
        set;
    }


    [ObservableProperty]
    public partial int Indent { get; set; } = 4;


    [ObservableProperty]
    public partial string SerializedContent { get; set; } = "";


    public ObservableCollection<string> BreadcrumbItems { get; } = [];


    public event EventHandler<MetadataDocumentLoadedEventArgs>? DocumentLoaded;


    protected async override Task SaveFile()
    {
        string? savePath = await App.GetService<IFilePikerService>()
            .SaveFileAsync(Common.Utils.FileFilters.Dms);

        if (string.IsNullOrEmpty(savePath))
        {
            return;
        }

        App.GetService<IFileService>().Save(savePath, SerializedContent, Encoding.UTF8);
    }


    private void SetInternal<T>(Action<T> callback, T value)
    {
        UpdatingInternal = true;
        callback(value);
        UpdatingInternal = false;
    }


    private async Task ReloadDocumentInternal()
    {
        if (UpdatingInternal)
        {
            return;
        }

        await LoadDocument(false);
        DocumentLoaded?.Invoke(this, new MetadataDocumentLoadedEventArgs(SerializedContent, true));
    }

    public static IProjectTask GetProjectTask()
    {
        if (CurrentTask != null && CurrentTask.TryGetTarget(out IProjectTask? task))
        {
            return task;
        }

        task = App.GetService<IProjectTaskService>().Allocate(ResourceKeys.MdmDocumentDisplayName.GetLocalized(), true);
        CurrentTask = new WeakReference<IProjectTask>(task);
        return task;
    }


    async partial void OnContextChanged(string value) => await ReloadDocumentInternal();


    async partial void OnLanguageChanged(string value) => await ReloadDocumentInternal();


    async partial void OnLabelTypeChanged(string value) => await ReloadDocumentInternal();


    partial void OnReadAllTriviaChanged(bool value)
    {
        if (UpdatingInternal)
        {
            return;
        }

        ReadOrIgnoreAllTrivia(value);
    }


    private async void ReadOrIgnoreAllTrivia(bool read)
    {
        UpdatingInternal = true;
        ReadHdata = read;
        ReadProperty = read;
        ReadTemplate = read;
        ReadStyle = read;
        ReadLabelStyle = read;
        ReadNote = read;
        UpdatingInternal = false;

        await ReloadDocumentInternal();
    }


    private async void TriviaOptionChanged(bool value)
    {
        if (UpdatingInternal)
        {
            return;
        }

        if (!value)
        {
            SetInternal(v => ReadAllTrivia = v, false);
        }

        await ReloadDocumentInternal();
    }


    partial void OnReadHdataChanged(bool value) => TriviaOptionChanged(value);


    partial void OnReadPropertyChanged(bool value) => TriviaOptionChanged(value);


    partial void OnReadTemplateChanged(bool value) => TriviaOptionChanged(value);


    partial void OnReadStyleChanged(bool value) => TriviaOptionChanged(value);

    partial void OnReadLabelStyleChanged(bool value) => TriviaOptionChanged(value);

    partial void OnReadNoteChanged(bool value) => TriviaOptionChanged(value);


    async partial void OnIndentChanged(int value) => await ReloadDocumentInternal();


    private MetadataSerializeOptions GetOptions()
    {
        MetadataElementFlags flags = MetadataElementFlags.None;

        if (ReadHdata)
        {
            flags |= MetadataElementFlags.HData;
        }

        if (ReadProperty)
        {
            flags |= MetadataElementFlags.Properties;
        }

        if (ReadTemplate)
        {
            flags |= MetadataElementFlags.Templates;
        }

        if (ReadStyle)
        {
            flags |= MetadataElementFlags.Styles;
        }

        if (ReadLabelStyle)
        {
            flags |= MetadataElementFlags.LabelStyles;
        }

        if (ReadNote)
        {
            flags |= MetadataElementFlags.Notes;
        }


        return new MetadataSerializeOptions
        {
            Indent = Indent,
            IgnoreMetadataSection = IgnoreMetadataSection,
            ElementFlags = flags,
            Context = Context,
            Language = Language,
            LabelType = LabelType,
            ReportService = GetProjectTask()
        };
    }


    private static void ResetCollection(ObservableCollection<string> target, IEnumerable<string> source)
    {
        target.Clear();
        foreach (string item in source)
        {
            target.Add(item);
        }
    }


    protected async override Task<bool> OnCurrentFileNameChangedOverride(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            return false;
        }

        bool isHistoryExist = await OptionalFileService.IsHistoryAsync(value);
        await LoadDocument(true);
        DocumentLoaded?.Invoke(this, new MetadataDocumentLoadedEventArgs(SerializedContent, false));

        BreadcrumbItems.Clear();
        BreadcrumbItems.Add(value);

        return !isHistoryExist;
    }


    private static void LogDocumentInformation(IMetadataDocument document) =>
        GetProjectTask().Log($"已读取 {document.Fields.Count} 个字段。");


    private async Task LoadDocument(bool updateContexts)
    {
        if (string.IsNullOrEmpty(CurrentFileName))
        {
            return;
        }

        using CancellationTokenSource? tokenSource = new();
        tokenSource.CancelAfter(1000);

        IsLoading = true;
        IMetadataSerializer serializer = App.GetService<IMetadataSerializer>();

        IMetadataDocument? document = await Task.Run(() => serializer.Deserialize(CurrentFileName), tokenSource.Token)
            .ContinueWith(task =>
            {
                if (task.IsCanceled)
                {
                    return null;
                }

                return task.Result;
            });

        if (document == null)
        {
            await App.GetService<IMessageService>().ShowAsync(ResourceKeys.AppTaskTimeout.GetLocalized(), null);
            return;
        }

        LogDocumentInformation(document);

        if (updateContexts)
        {
            // 更新上下文集合
            ResetCollection(Contexts, document.Contexts.Select(c => c.Name));
            ResetCollection(Languages, document.Languages.Select(l => l.Name));
            ResetCollection(LabelTypes, document.LabelTypes.Select(t => t.Name));
            // 更新当前上下文
            SetInternal(ctx => Context = ctx, document.Context);
            SetInternal(lang => Language = lang, document.Language);
            SetInternal(lbt => LabelType = lbt, document.LabelType);
        }

        serializer.Options = GetOptions();
        // 序列化
        SerializedContent = await Task.Run(() => serializer.Serialize(document));

        IsLoading = false;
    }
}